package com.hoe.tsb.fraft;

import com.hoe.tsb.Configuration;
import com.hoe.tsb.fraft.exception.FRaftException;
import lombok.extern.slf4j.Slf4j;

import java.io.Closeable;
import java.io.IOException;
import java.util.Random;

/**
 * <p>
 * 循环计入的定时器
 * </p>
 *
 * @author hoe
 * @version 1.0
 * @date 2022/11/22 17:16
 */
@Slf4j
public class CircleTimer implements Closeable {

    private static CircleTimer mTimer;
    private boolean willClose = false;
    private Thread timerThread;
    private Configuration mConfig = Configuration.getDefault();
    private Runnable mCallback;
    private boolean reset = false;
    private Random mRand = new Random();

    private CircleTimer() {
        timerThread = new Thread("timerThread") {
            @Override
            public void run() {
                while(!willClose) {
                    synchronized (CircleTimer.this) {
                        reset = false;
                        try {
                            CircleTimer.this.wait(randSleepMill());
                        } catch (InterruptedException e) {
                            reset = true;
                        }

                        if (!reset) {
                            if (mCallback != null) {
                                //超时回调
                                mCallback.run();
                            } else {
                                log.warn("定时器超时回调没有设置");
                            }
                        }
                    }
                }
                log.info("CircleTimer关闭成功");
            }
        };
        timerThread.start();
    }

    public static CircleTimer get(Configuration config, Runnable callback) {
        CircleTimer circleTimer = get();
        circleTimer.mConfig = config;
        circleTimer.mCallback = callback;
        return circleTimer;
    }

    public static CircleTimer get() {
        if (mTimer != null) {
            return mTimer;
        }
        synchronized (CircleTimer.class) {
            if (mTimer == null) {
                mTimer = new CircleTimer();
            }
            return mTimer;
        }
    }

    @Override
    public void close() throws IOException {
        willClose = true;
    }

    /**
     * 定时器还没到时可以随时进行重置
     */
    public void reset() {
        synchronized (CircleTimer.this) {
            log.debug("计时器被重置");
            reset = true;
            CircleTimer.this.notifyAll();
        }
    }

    /**
     * 获取一个介于心跳时长和超时时长的随机数
     * @return 随机数
     */
    private long randSleepMill() {
        Integer timeout = mConfig.getTimeout();
        Integer hfc = mConfig.getHfc();
        if (hfc >= timeout) {
            throw new FRaftException("FRaft的超时时长务必大于心跳频率时长");
        }

        while(true) {
            int mill = mRand.nextInt(timeout);
            if (mill > mConfig.getHfc()) {
                log.debug("定时器即将等待{}毫秒...", mill);
                return mill;
            }
        }
    }
}
